/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.devs.benchmark.topology;

import java.util.ArrayList;
import java.util.List;
import org.devs.benchmark.Util;
import org.devs.benchmark.modeling.ConnectionType;
import org.devs.benchmark.modeling.CoupledType;
import org.devs.benchmark.modeling.ModelType;

/**
 *
 * @author Alejandro Moreno
 */
public class NKCube extends Network {

    int dimension;
    int processors;

    public NKCube(String name, int dimension, int processors, Class message, int recursion) throws Exception {

        super(name, message, recursion);

        if (dimension < 1 || processors < 1 || recursion < 1) {
            throw new Exception("Parameters are incorrect");
        }

        this.dimension = dimension;
        this.processors = processors;


    }

    public NKCube(String name, int dimension, int processors, Class message) throws Exception {

        super(name, message);

        if (dimension < 1 || processors < 1) {
            throw new Exception("Parameters are incorrect");
        }

        this.dimension = dimension;
        this.processors = processors;

    }

    public int getDimension() {
        return dimension;
    }

    public void setDimension(int dimension) {
        this.dimension = dimension;
    }

    public int getProcessors() {
        return processors;
    }

    public void setProcessors(int processors) {
        this.processors = processors;
    }




    public void buildModel(){
        super.setModel(createNetwork(this.name, this.dimension, this.processors, super.message, super.recursion));
    }

    public void connectNodes(CoupledType coupled, int processors, int dimension, List<List<ModelType>> structs) {


        for (int j = 0; j < Math.pow(processors, dimension - 1); j++) {

            for (int i = 0; i < structs.size(); i++) {
                int q = (i + 1) % structs.size();

                List<ModelType> struct1 = structs.get(i);
                List<ModelType> struct2 = structs.get(q);

                coupled.getConnections().add(new ConnectionType(struct1.get(j), struct1.get(j).getOutports().get((dimension - 1) * 2), struct2.get(j), struct2.get(j).getInports().get(((dimension - 1) * 2) + 1)));
                coupled.getConnections().add(new ConnectionType(struct2.get(j), struct2.get(j).getOutports().get(((dimension - 1) * 2) + 1), struct1.get(j), struct1.get(j).getInports().get((dimension - 1) * 2)));


            }
        }

    }

    public void connect(CoupledType coupled, int processors, int dimension, List<ModelType> models) {

        if (dimension > 0) {

            List<List<ModelType>> structs = Util.partition(models, (int)Math.pow(processors, dimension - 1));
            connectNodes(coupled, processors, dimension, structs);

            for (int j = 0; j < structs.size(); j++)
                connect(coupled, processors, dimension - 1, structs.get(j));

        }
    }

    public List<ModelType> createStruct(CoupledType coupled, String name, int dimension, int processors, int connectivity, String id, Class message) {

        List<ModelType> struct = new ArrayList<ModelType>();

        if (dimension > 0) {

            List<List<ModelType>> structs = new ArrayList<List<ModelType>>();
            for (int i = 0; i < processors; i++) {
                structs.add(createStruct(coupled, name, dimension - 1, processors, connectivity, id + "_" + i, message));

            }
            connectNodes(coupled, processors, dimension, structs);
            for (int i = 0; i < processors; i++) {
                struct.addAll(structs.get(i));
            }

        } else {
            ModelType node = buildNode(name, connectivity, message, id);
            coupled.getModels().add(node);
            struct.add(node);

        }

        return struct;

    }

    public ModelType createNetwork(String name, int dimension, int processors, Class message, int recursion) {
        return create(name, dimension, processors, "", message, recursion);
    }

    public ModelType create(String name, int dimension, int processors, String id, Class message, int recursion) {

        CoupledType coupled = null;

        if (recursion > 0) {

            coupled = new CoupledType(name + "_Coupled" + id, Util.createInports(2*dimension, message), Util.createOutports(2*dimension, message));

            for (int i = 0; i < Math.pow(processors, dimension); i++) {
                coupled.getModels().add(create(name, dimension, processors, id + "_" + i, message, recursion - 1));
            }

            connect(coupled, processors, dimension, coupled.getModels());

        } else {


            coupled = buildNode(name, 2*dimension, message, id);


        }

        return coupled;

    }
}
